package com.pic.mgt.queue;

import java.util.LinkedList;
import java.util.Queue;

public abstract class QueueService<T> implements Runnable {
	// Queue 
	protected Queue<T> queue = new LinkedList<T>();
	private boolean flag = false; // 記錄 Queue 是否在 Start 的狀態
	
	private Thread thread;
	private String threadName;
	
	public QueueService(String threadName) {
		this.threadName = threadName;
	}
	
	public synchronized void startQueueService() {
		// 不能同時有二個 Monitored File List Handler Thread
		if (!this.flag) {
			this.thread = new Thread(this);
			this.thread.setName(this.threadName);
			this.thread.setDaemon(true);
			
			clearQueue();
			
			flag = true;
			
			this.thread.start();
		}			
	}
	
	/**
	 * STOP 機制並不會立刻做Stop，會停留在此方法，並
	 * 等  Queue 內的資料都被處理完後，才會Stop。
	 */
	public synchronized void stopQueueService() {
		// 如果 thread 沒有起來,不須動作
		if (!this.flag) {
			return;
		}
			
		flag = false;
		
		synchronized(this.queue) {
			/*
			 * 這裡做同步的目的,是要確保 flag 設定為 false 能與 getElementFromQueue() 方法
			 * 有所同步,才不會造成直接 join 後,有可能變成死結。
			 */
			
			if (this.thread.getState() == Thread.State.WAITING) {
				/*
				 * 如果在 waiting 情況下，表示  queue內沒有資料，
				 * 可以直接中斷。
				 */
				this.thread.interrupt();
			}
		}
		
		try {
			/*
			 * 若 Thread 不是在 WAITING 情況下，仍然在執行中，則表示
			 * 內還有資料，並且正在處理，必須等待，queue 內的資料被處理完。
			 * 若之前是在 WAITING 情況下，則會在之前就被 Interrupt 掉，就算
			 *  JOIN 也不會 BLOCK 住，因為 Thread 已被  Interrupt 掉了。
			 */
			this.thread.join();
		} catch (InterruptedException e) {
			// TODO 寫 log
			e.printStackTrace();
		}
	}
	
	/**
	 * 放入 Element 到 Queue中。(如果 Queue Service 還沒 Start 起來，
	 * 則呼叫此方法將不會有作用，也不會放入 Queue 中。)
	 * @param element
	 */
	public void putElementInQueue(T element) {
		if (this.flag) {
			synchronized (this.queue) {
				this.queue.offer(element);
				this.queue.notify();
			}
		}
	}
	
	private T getElementFromQueue()
						throws InterruptedException {
		synchronized (this.queue) {
			if (this.queue.isEmpty()) {
				if (this.flag) {
					this.queue.wait();
				}
				else {
					throw new InterruptedException();
				}	
			}
			return this.queue.poll();
		}
	}
	
	public void clearQueue() {
		synchronized(this.queue) {
			this.queue.clear();
		}
	}
	
	public synchronized boolean isWorking() {
		return this.flag;
	}
	
	/**
	 * 當 Queue 內有資料時,會觸動此方法。
	 * @param element Queue 內的資料元素
	 */
	protected abstract void newElementArrive(T element);
	
	@Override
	public void run() {
		T element;
		while(true) {			
			try {
				/*
				 *  從 Queue 中取出 Element。
				 *  會停留在此方法，一直到有資料取得。
				 *  throws InterruptedException
				 */
				element = getElementFromQueue();
			} catch (InterruptedException e) {
				/*e.printStackTrace();*/
				break;
			}
			newElementArrive(element);
		};
	}
}
